<?php

// Copyright 2022 Hackware SpA <human@hackware.cl>
// This file is part of "Hackware Web Services Payment" and licensed under
// the terms of the GNU Affero General Public License version 3, or (at your
// option) a later version. You should have received a copy of this license
// along with the software. If not, see <https://www.gnu.org/licenses/>.

namespace Hawese\Payment\Gateways;

use Hawese\Payment\Payment;
use Hawese\Payment\Exceptions\UnexpectedResponseException;
use Hawese\Payment\Support\Http as HttpSupport;
use Hawese\Core\User;
use Brick\Money\Money;
use Illuminate\Support\Str;
use Psr\Http\Message\ResponseInterface;

class PayPalGateway extends AbstractGateway
{
    public function purchase(
        array $bodyParams,
        string $origin = 'wallet'
    ): array {
        $bodyParams = $this->purchaseParams($bodyParams, $origin);
        $response = $this->request('POST', '/v2/checkout/orders', $bodyParams);
        $responseBody = $this->purchaseProcessResponse($response);

        $paymentUuid = $this->purchaseInsertPayment(
            $bodyParams,
            $responseBody,
            $origin
        );

        foreach ($responseBody->links as $link) {
            if ($link->rel === 'approve') {
                $redirectUrl = $link->href;
                break;
            }
        }
        return ['uuid' => $paymentUuid, 'redirect_url' => $redirectUrl];
    }

    private function purchaseParams(
        array $bodyParams,
        string $origin
    ): array {
        $uuid = (string) Str::uuid();
        // 'uuid' => $uuid
        $returnUrl = route('gateways.notify', ['gateway' => 'paypal']);

        return [
            'intent' => 'CAPTURE',
            'purchase_units' => [
                [
                    'reference_id' => $uuid,
                    'amount' => [
                        'currency_code' => $bodyParams['currency_code'],
                        'value' => $bodyParams['amount']
                    ],
                    'description' => $bodyParams['description']
                ]
            ],
            'payer' => [
                'email_address' => $bodyParams['email_address']
            ],
            'application_context' => [
                'landing_page' => 'BILLING',
                'shipping_preference' => 'NO_SHIPPING',
                'cancel_url' => config("payment.origins.$origin.return_url"),
                'return_url' => $returnUrl
            ]
        ];
    }

    /**
     * Validate and process $response
     * @returns object parsed body
     */
    private function purchaseProcessResponse(
        ResponseInterface $response
    ): object {
        if ($response->getStatusCode() === 201) {
            $responseBody = HttpSupport::getJsonBody($response);

            if (property_exists($responseBody, 'id')) {
                return $responseBody;
            }
        }

        throw new UnexpectedResponseException($response);
    }

    /**
     * @returns string Payment primary key value
     */
    private function purchaseInsertPayment(
        array $bodyParams,
        object $responseBody,
        string $origin
    ): string {
        return (new Payment([
            'uuid' => $bodyParams['purchase_units'][0]['reference_id'],
            'user_uid' =>
                User::findByEmailOrCreate(
                    $bodyParams['payer']['email_address']
                )->uid,
            'gateway' => 'paypal',
            'currency' =>
                $bodyParams['purchase_units'][0]['amount']['currency_code'],
            'amount' => $bodyParams['purchase_units'][0]['amount']['value'],
            'description' => $bodyParams['purchase_units'][0]['description'],
            'detail' => ['id' => $responseBody->id],
            'status' => Payment::STATUS_PENDING,
            'origin' => $origin,
        ]))->insert();
    }

    public function acceptNotification(array $bodyParams)
    {
        $response = $this->request(
            'POST',
            "/v2/checkout/orders/{$bodyParams['token']}/capture",
            null
        );

        $responseBody = HttpSupport::getJsonBody($response);

        $responsePurchaseUnit = $responseBody->purchase_units[0];
        $payment = Payment::find($responsePurchaseUnit->reference_id);
        $responseAmount = $responsePurchaseUnit->payments->captures[0]->amount;

        if (
            Money::of($responseAmount->value, $responseAmount->currency_code)
                ->isAmountAndCurrencyEqualTo($payment->amountAsMoney)
        ) {
            $payment->validateAndUpdateStatus(
                $responseBody->status,
                ['COMPLETED']
            );
        } else {
            throw new UnexpectedResponseException($response);
        }

        header('Location: ' . HttpSupport::appendQueryParams(
            config("payment.origins.{$payment->origin}.return_url"),
            ['uuid' => $payment->uuid]
        ));
    }
}
